﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using ServerAdapter.WcfGeebaService;
using ServerAdapter;

namespace GeebaClient
{
    public class ConsoleClient
    {
        #region DataMembers

        private ServerAdapter.SAdapter server;
        private List<GSubForum> subForums;
        private GSubForum currSubForum;
        private User user;
        private ClientListener listener;

        #endregion

        #region Cstor

        public ConsoleClient()
        {
            listener = new ClientListener();
            listener.OnUpdateFromServer += new ClientListener.OnUpdate(listener_OnUpdateFromServer);
            listener.OnEditMessageFromServer += new ClientListener.OnEditMessage(listener_OnEditMessageFromServer);
            server = new ServerAdapter.SAdapter(listener);
            
            this.user = null;
            this.subForums = new List<GSubForum>();
            this.currSubForum = null;

        }

        void listener_OnEditMessageFromServer(string subforumTopic, GThread message, DateTime timestamp)
        {
            Console.WriteLine("message edited from server, subforumTopic: " + subforumTopic + " subject: \"" + message._subject + "\" by: \"" + message._publisher.username + "\" at " + timestamp.ToShortTimeString());
        }

        void listener_OnUpdateFromServer(GThread message, DateTime timestamp, bool isNotification)
        {
            Console.WriteLine("message published, subject: \"" + message._subject + "\" by: \"" + message._publisher.username + "\" at " + timestamp.ToShortTimeString());
        }

        public void Dispose()
        {
            server.Dispose();
        }

        #endregion

        #region ClientMethods
        public void run()
        {
            try
            {
                entry();
                do
                {
                    showMenu();
                    string lineFromUser = Console.ReadLine();
                    if (lineFromUser.Length == 1)
                    {
                        lineFromUser = "0" + lineFromUser;
                    }
                    switch (lineFromUser.Substring(0, 2))
                    {
                        case "00": //exit
                            this.Dispose();
                            //close server
                            return;

                        case "01": //register
                            Registration();
                            break;

                        case "02": //login
                            Login();
                            break;

                        case "03": //logout
                            Logout();
                            break;

                        case "04": //view sub forums and topics
                            ViewSubforums();
                            break;

                        case "05": //enter sub forum and display threads
                            EnterSubForum();
                            break;

                        case "06": //publish post
                            Publish();
                            break;

                        case "07": //7.  Remove Thread
                            RemoveThread();
                            break;

                        case "08": //ChangePassword
                            ChangePassword();
                            break;

                        case "09": //BumpThread
                            BumpThread();
                            break;

                        case "10":
                            CommentOnThread();
                            break;

                        case "11":
                            EditThread();
                            break;

                        // Admin :

                        case "12": //Add Moderator
                            AddModerator();
                            break;

                        case "13": //Remove Moderator
                            RemoveModerator();
                            break;

                        case "14": //Replace Moderator
                            ReplaceModerator();
                            break;

                        case "15": //Get Thread Count
                            GetThreadCount();
                            break;

                        case "16": //Get Thread Count
                            GetMemberThreadCount();
                            break;

                        case "17": //AddSubForum
                            AddSubForum();
                            break;

                        case "18": //RemoveSubForum
                            RemoveSubForum();
                            break;

                        default:
                            Console.WriteLine("Unknown command.");
                            break;
                    }
                } while (true);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception: " + e.Message + e.StackTrace);
            }
        }

        private void showMenu()
        {
            Console.WriteLine("       ");
            Console.WriteLine("        Menu  ");
            Console.WriteLine("-------------------------------------------------------");
            Console.WriteLine("    0.  Exit");
            Console.WriteLine("    1.  Register");
            Console.WriteLine("    2.  Login");
            Console.WriteLine("    3.  Logout");
            Console.WriteLine("    4.  View sub forums and topics");
            Console.WriteLine("    5.  Enter sub forum and show posts");
            if (user.userType == UserType.Member || (user.userType == UserType.Moderator || (user.userType == UserType.Admin)))
            {
                Console.WriteLine("    6.  Publish thread");
                Console.WriteLine("    ------------!!--NEW--!!-----------");
                Console.WriteLine("    7.  Remove Thread");
                Console.WriteLine("    8.  Change password");
                Console.WriteLine("    9.  Bump thread");
                Console.WriteLine("    10. Comment on thread");
                Console.WriteLine("    11. Edit thread");

            }
            if (user.userType == UserType.Admin)
            {
                Console.WriteLine("    -------ADMIN-------");
                Console.WriteLine("    12. Add Moderator");
                Console.WriteLine("    13. Remove Moderator");
                Console.WriteLine("    14. Replace Moderator");
                Console.WriteLine("    15. Get total thread count");
                Console.WriteLine("    16. Get member's thread count");
                Console.WriteLine("    17. Add SubForum");
                Console.WriteLine("    18. Remove SubForum");
            }
        }

        /// <summary>
        /// 1. entry
        /// </summary>
        public void entry()
        {
            this.user = server.serverEntry();
            server.serverSubscribe(this.user.username);
            Console.WriteLine(@"**************************************************************");
            Console.WriteLine(@"*                   ___                __                    *");
            Console.WriteLine(@"*                  / __|___  ___  __  /..\                   *");
            Console.WriteLine(@"*                 | (_ / -_\/ -_\| _`| || |                  *");
            Console.WriteLine(@"*                  \___\___/\___/|__,|_/\_| (c)              *");
            Console.WriteLine(@"*                                                            *");
            Console.WriteLine(@"**************************************************************");
            Console.WriteLine("    " + this.user.username + ", - Welcome to Geeba Forum, " + DateTime.Now);
            Console.WriteLine(@"**************************************************************");
        }

        public void Registration()
        {
            string username;
            string password;
            string secondPassword;
            Console.WriteLine("Please insert you desired Username:");
            username = Console.ReadLine();
            do
            {
                Console.WriteLine("Select your password:");
                password = ReadPassword();
                Console.WriteLine("Re enter your password:");
                secondPassword = ReadPassword();
            } while (password != secondPassword);
            try
            {
                bool regSuccess = server.serverRegister(username, password);
                if (regSuccess)
                {
                    Console.WriteLine("Registration was successful.");
                }
                else
                {
                    Console.WriteLine("Registration failed.");
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Registration failed. " + e.Message);
                }
            }
        }
        
        public void Login()
        {
            string password,username;
            User oldUser = this.user;

            if (this.user != null && this.user.userType != UserType.Guest)
            {
                Console.WriteLine("Already logged in.");
            }
            else
            {
                Console.WriteLine("Username:");
                username = Console.ReadLine();
                Console.WriteLine("Password:");
                password = ReadPassword();
                try
                {
                    this.user = server.serverLogin(oldUser, username, password);
                    server.serverSubscribe(this.user.username);
                    if (this.user != null)
                    {
                        Console.WriteLine("Login successful. Logged in as " + this.user.username + " (" + this.user.userType +").");
                    }
                    else
                    {
                        Console.WriteLine("Login failed.");
                        this.user = oldUser;
                    }
                }
                catch (Exception e)
                {
                    if (e != null)
                    {
                        Console.WriteLine("Login failed. " + e.Message);
                        this.user = oldUser;
                    }
                }
            }
        }

        public void Logout()
        {
            try
            {
                if (this.user == null || this.user.userType == UserType.Guest)
                {
                    throw new Exception("Not logged in.");
                }
                User newUser = server.serverLogout(this.user); // returns a user of type guest
                if (newUser != null)
                {
                    Console.WriteLine("Logout successful.");
                    this.user = newUser;
                }
                else
                {
                    Console.WriteLine("Logout failed.");
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Logout failed. " + e.Message);
                }
            }
        }

        /// <summary>
        /// show sub forums list and their topics
        /// </summary>
        public void ViewSubforums()
        {
            try
            {
                if (this.user == null)// || this.user.userType == UserType.Guest)
                {
                    throw new Exception("Not logged in.");
                }
                this.subForums = server.serverGetSubForums();
                Console.WriteLine("Sub Forums:");
                Console.WriteLine("-------------------------------");
                foreach (GSubForum subForum in this.subForums)
                {
                    Console.WriteLine("Topic: " + subForum.topic);
                    Console.WriteLine("-------------------------------");
                }
                Console.WriteLine("End of Sub Forums list.");
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Show sub forums failed. " + e.Message);
                }
            }
        }

        private void EnterSubForum()
        {
            try
            {
                Console.WriteLine("Choose sub forum topic");
                string currSubforumTopic = Console.ReadLine();
                this.currSubForum = this.subForums.Find(delegate(GSubForum subForum)
                                                    {
                                                        return subForum.topic == currSubforumTopic;
                                                    });
                if (this.currSubForum == null)
                {
                    Console.WriteLine("Sub Forum Name does not exist.");
                }
                else
                {
                    Console.WriteLine("Topic: " + this.currSubForum.topic);
                    Console.WriteLine("-------------------------------");
                    Console.WriteLine("   |");
                    IEnumerable<GThread> threads = server.getThreads(this.user, this.currSubForum);
                    threads = threads.OrderByDescending(thread => thread._modifiedDate);
                    foreach (GThread thread in threads)
                    {
                        Console.WriteLine("   |");
                        Console.WriteLine(@"   \---->ID: " + thread._id);
                        Console.WriteLine("         Subject: " + thread._subject);
                        Console.WriteLine("         published date : " + thread._publishDate);
                        if (thread._modifiedDate != null)
                        {
                            Console.WriteLine("         modified date : " + thread._modifiedDate);
                        }
                        Console.WriteLine("         published by   : " + thread._publisher.username);
                        Console.WriteLine("         -------------");
                        Console.WriteLine("         Content:" + thread._content);
                        Console.WriteLine("         -------------");

                        foreach (GPost post in server.serverGetComments(this.user, currSubForum.topic, thread._id))
                        {
                            Console.WriteLine("               |");
                            Console.WriteLine(@"               \---->Post ID: " + post._id);
                            Console.WriteLine("                      Subject: " + post._subject);
                            Console.WriteLine("                      published date : " + post._publishDate);
                            Console.WriteLine("                      published by   : " + post._publisher.username);
                            Console.WriteLine("                      -------------");
                            Console.WriteLine("                      Content:" + post._content);
                            Console.WriteLine("                      -------------");
                        }
                    }
                    Console.WriteLine("End of thread list");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("EnterSubForum Failed." + e.Message );
            }
        }

        /// <summary>
        /// publish a thread in a sub forum
        /// </summary>
        /// <param name="postToPublish"></param>
        /// <param name="subforum"></param>
        public void Publish()
        {
            if (this.user == null || this.user.userType == UserType.Guest)
            {
                Console.WriteLine("Must be logged in to publish.");
                return;
            }
            if (this.currSubForum == null)
            {
                Console.WriteLine("You must first select a sub forum.");
                return;
            }
            try
            {
                //GThread
                Console.WriteLine("Subject:");
                string subject = Console.ReadLine();
                Console.WriteLine("Content:");
                string content = Console.ReadLine();
                GThread newMessage = new GThread ();
                newMessage._subject = subject;
                newMessage._content = content;
                newMessage._publisher = (Member)this.user;// new Member();
                //newMessage._publisher.username = this.user.username;
                server.serverPublish((Member)this.user, this.currSubForum.topic, newMessage);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Publish post failed. " + e.Message);
                }
            }
        }

        //new methods for version 2:

        //      7.  Remove Thread
        public void RemoveThread()
        {

            if (currSubForum == null)
            {
                Console.WriteLine("RemoveThread failed - you must be logged in to remove a Thread. ");
                return;
            }
            try 
	        {	        
		        Console.WriteLine("insert thread id to delete");
                int threadID = Int32.Parse(Console.ReadLine());
                server.serverRemoveThread((Member)this.user, this.currSubForum.topic, threadID);
	        }
	        catch (Exception e)
            {
		         if (e != null)
                {
                    Console.WriteLine("Remove Thread failed. " + e.Message);
                }
	        }
        }

        public void ChangePassword()
        {
            if (this.user == null || this.user.userType == UserType.Guest)
            {
                Console.WriteLine("Change Password failed - you must be logged in to change password. ");
                return;
            }

            string oldpassword;
            string newPassword;
            string secondPassword;
            Console.WriteLine("Please insert your old password:");
            oldpassword = ReadPassword();
            do
            {
                Console.WriteLine("Select your new password:");
                newPassword = ReadPassword();
                Console.WriteLine("Re enter your password:");
                secondPassword = ReadPassword();
            } while (newPassword != secondPassword);
            try
            {
                bool changePassSuccess = server.changePassword((Member)this.user,oldpassword, newPassword);
                if (changePassSuccess)
                {
                    Console.WriteLine("Change Password successful.");
                }
                else
                {
                    Console.WriteLine("Change Password failed.");
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Change Password failed. " + e.Message);
                }
            }
        }

        public void BumpThread()
        {
            if (this.user == null || this.user.userType == UserType.Guest)
            {
                Console.WriteLine("BumpThread failed - you must be logged in to Bump Thread. ");
                return;
            }

            if (this.currSubForum == null)
            {
                Console.WriteLine("BumpThread failed - you must be inside a sub forum to bump a thread. ");
                return;
            }


            try
            {

                Console.WriteLine("Please choose thread ID to bump");
                int threadID = Int32.Parse(Console.ReadLine());

                bool BumpThreadSuccess = server.serverBumpThread((Member)user, currSubForum.topic, threadID);
                if (BumpThreadSuccess)
                {
                    Console.WriteLine("Bump Thread successful.");
                }
                else
                {
                    Console.WriteLine("BumpThread failed.");
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("BumpThread failed. " + e.Message);
                }
            }
        }

        public void CommentOnThread()
        {
            if (this.user == null || this.user.userType == UserType.Guest)
            {
                Console.WriteLine("CommentOnThread failed - you must be logged in to Comment On a Thread. ");
                return;
            }

            if (this.currSubForum == null)
            {
                Console.WriteLine("CommentOnThread failed - you must be inside a sub forum to Comment On a Thread. ");
                return;
            }


            try
            {

                Console.WriteLine("Please choose thread ID to comment on");
                int threadID = Int32.Parse(Console.ReadLine());
                GPost comment = new GPost();

                Console.WriteLine("Please write comment subject");
                comment._subject = Console.ReadLine();

                Console.WriteLine("Please write comment content");
                comment._content = Console.ReadLine();

                //Console.WriteLine("Please write comment content");
                comment._publishDate = System.DateTime.Now;
                comment._modifiedDate = comment._publishDate;
                comment._publisher = (Member)this.user;
                comment._id = -1;

                //Console.WriteLine("Please write comment content");
                //comment._content = Console.ReadLine();

                int commentID = server.commentOnThread((Member)user, comment, currSubForum.topic, threadID);
                if (commentID >= 0)
                {
                    Console.WriteLine("CommentOnThread successful. comment ID " + commentID);
                }
                else
                {
                    Console.WriteLine("CommentOnThread failed.");
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("CommentOnThread failed. " + e.Message);
                }
            }
        }


        // 11
        public void EditThread()
        {
            if (this.user == null || this.user.userType == UserType.Guest)
            {
                Console.WriteLine("EditThread failed - you must be logged in to Edit a Thread. ");
                return;
            }

            if (this.currSubForum == null)
            {
                Console.WriteLine("EditThread failed - you must be inside a sub forum to Edit a Thread. ");
                return;
            }

            try
            {
                Console.WriteLine("Please choose thread ID to Edit");
                int threadID = Int32.Parse(Console.ReadLine());
                GPost comment = new GPost();

                Console.WriteLine("Please write Thread's new content");
                string newContent = Console.ReadLine();

                bool serverThreadEditContentSucess = server.serverEditContent((Member)user, currSubForum.topic, threadID, newContent);
                if (serverThreadEditContentSucess)
                {
                    Console.WriteLine("EditThread successful.");
                }
                else
                {
                    Console.WriteLine("EditThread failed.");
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("EditThread failed. " + e.Message);
                }
            }
        }

        //      12.  Add Moderator
        public void AddModerator()
        {
            try
            {
                if (currSubForum == null)
                {
                    Console.WriteLine("Add Moderator failed. You must be inside sub forum to add moderator.");
                    return;
                }
                Console.WriteLine("insert new moderator username");
                string newModeratorUserName = Console.ReadLine();
                server.serverAddModerator((Member)this.user, this.currSubForum.topic, newModeratorUserName);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Add Moderator failed. " + e.Message);
                }
            }
        }

        //      13.  Remove Moderator
        public void RemoveModerator()
        {
            try
            {
                if (currSubForum == null)
                {
                    Console.WriteLine("Remove Moderator failed. You must be inside sub forum to remove moderator.");
                    return;
                }
                Console.WriteLine("insert moderator username to remove");
                string moderatorUserName = Console.ReadLine();
                server.serverAddModerator((Member)this.user, this.currSubForum.topic, moderatorUserName);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Remove Moderator failed. " + e.Message);
                }
            }
        }

        //      14. Replace Moderator
        public void ReplaceModerator()
        {
            try
            {
                if (currSubForum == null)
                {
                    Console.WriteLine("Replace Moderator failed. You must be inside sub forum to Replace moderator.");
                    return;
                }
                Console.WriteLine("insert old moderator username");
                string OldModeratorUserName = Console.ReadLine();
                Console.WriteLine("insert new moderator username");
                string NewModeratorUserName = Console.ReadLine();
                server.serverReplaceModerator((Member)this.user, this.currSubForum.topic, OldModeratorUserName, NewModeratorUserName);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Replace Moderator failed. " + e.Message);
                }
            }
        }

        //      15. Get total thread count
        public void GetThreadCount()
        {
            try
            {
                if (currSubForum == null)
                {
                    Console.WriteLine("GetThreadCount failed. You must be inside sub forum to GetThreadCount.");
                    return;
                }
                int threadCount = server.serverGetThreadCount((Member)this.user, this.currSubForum.topic);
                Console.WriteLine("Sub Forum " + this.currSubForum.topic + " Thread Count: " + threadCount);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("GetThreadCount failed. " + e.Message);
                }
            }
        }

        //      16. Get member's thread count
        public void GetMemberThreadCount()
        {
            try
            {
                Console.WriteLine("Insert Member Name");
                string memberName = Console.ReadLine();
                Member member = new Member();
                member.username = memberName;

                int threadCount = server.serverGetMemberThreadCount((Member)this.user, memberName);
                Console.WriteLine("Member " + memberName + " Thread Count: " + threadCount);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("GetMemberThreadCount failed. " + e.Message);
                }
            }
        }
        
        //      17. Add SubForum
        public void AddSubForum()
        {
            try
            {
                Console.WriteLine("insert new SubForum topic");
                string topic =  Console.ReadLine();
                Console.WriteLine("insert new sub forum's moderator username");
                string moderatorUsername = Console.ReadLine();
                server.serverAddSubForum((Member)this.user, topic, moderatorUsername);
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Add Moderator failed. " + e.Message);
                }
            }
        }

        //      18. Remove SubForum
        public void RemoveSubForum()
        {
            try
            {
                Console.WriteLine("insert SubForum topic to remove");
                string topic = Console.ReadLine();
                server.serverRemoveSubForum((Member)this.user, topic);
                if (this.currSubForum.topic == topic)
                {
                    this.currSubForum = null;
                }
            }
            catch (Exception e)
            {
                if (e != null)
                {
                    Console.WriteLine("Remove Moderator failed. " + e.Message);
                }
            }
        }



        #endregion

        #region Utils

        public static string ReadPassword() {
            Stack<string> passbits = new Stack<string>();
            //keep reading
            for (ConsoleKeyInfo cki = Console.ReadKey(true); cki.Key != ConsoleKey.Enter; cki = Console.ReadKey(true)) {
                if (cki.Key == ConsoleKey.Backspace) {
                    //rollback the cursor and write a space so it looks backspaced to the user
                    Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                    Console.Write(" ");
                    Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                    passbits.Pop();
                }
                else {
                    Console.Write("*");
                    passbits.Push(cki.KeyChar.ToString());
                }
            }
            string[] pass = passbits.ToArray();
            Array.Reverse(pass);
            Console.WriteLine();
            return string.Join(string.Empty, pass);
        }

        #endregion 

    }
}
